#ifndef __CHANNEL_H__
#define __CHANNEL_H__

#include <queue>
#include <mutex>
#include <condition_variable>

template<typename T>
class Channel
{
private:
    std::queue<T> queue_;
    std::mutex mtx_;
    std::condition_variable cv_producer_;
    std::condition_variable cv_consumer_;
    size_t capacity_;
    bool close_ = false;

public:
    Channel(size_t capacity = 0) : capacity_(capacity) {} 

    bool Send(T value)
    {
        std::unique_lock<std::mutex> lock(mtx_);
        cv_producer_.wait(lock, [this]
        {
            return (capacity_ == 0 && queue_.empty()) || queue_.size() < capacity_ || close_;
        });

        if(close_)
        {
            return false;
        }

        queue_.push(value);
        cv_consumer_.notify_one();
        return true;
    }   

    bool Receive(T& value)
    {
        std::unique_lock<std::mutex> lock(mtx_);
        cv_consumer_.wait(lock, [this]
        {
            return !queue_.empty() || close_;
        });

        if(close_ && queue_.empty())
        {
            return false;
        }

        value = queue_.front();
        queue_.pop();
        cv_producer_.notify_one();
        return true;
    }

    void Close()
    {
        std::unique_lock<std::mutex> lock(mtx_);
        close_ = true;
        cv_producer_.notify_all();
        cv_consumer_.notify_all();
    }
};

#endif